/*
   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

/****************************************************************************
 *
 * This demo showcases BLE GATT client. It can scan BLE devices and connect to one device.
 * Run the gatt_server demo, the client demo will automatically connect to the gatt_server demo.
 * Client demo will enable gatt_server's notify after connection. The two devices will then exchange
 * data.
 *
 ****************************************************************************/

#include <stdint.h>
#include <string.h>
#include <stdbool.h>
#include <stdio.h>
#include "nvs.h"
#include "nvs_flash.h"

#include "esp_bt.h"
#include "esp_gap_ble_api.h"
#include "esp_gattc_api.h"
#include "esp_gatt_defs.h"
#include "esp_bt_main.h"
#include "esp_gatt_common_api.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "string.h"

#include "my_ble_client.h"

#define GATTC_TAG "GATTC_CLIENT"

/* Declare static functions */
static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param);
static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);
static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param);

/* {6E400001-B5A3-F393-E0A9-E50E24DCCA9E} */
static esp_bt_uuid_t server_service_uuid = {
    .len = ESP_UUID_LEN_128,
    .uuid = {
        .uuid128 = {0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0,
                    0x93, 0xf3, 0xa3, 0xb5, 0x01, 0x00, 0x40, 0x6e},
    },
};
/* {6E400002-B5A3-F393-E0A9-E50E24DCCA9E} */
static esp_bt_uuid_t tx_char_uuid = {
    .len = ESP_UUID_LEN_128,
    .uuid = {
        .uuid128 = {0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0,
                    0x93, 0xf3, 0xa3, 0xb5, 0x02, 0x00, 0x40, 0x6e},
    },
};
/* {6E400003-B5A3-F393-E0A9-E50E24DCCA9E} */
static esp_bt_uuid_t rx_char_uuid = {
    .len = ESP_UUID_LEN_128,
    .uuid = {
        .uuid128 = {0x9e, 0xca, 0xdc, 0x24, 0x0e, 0xe5, 0xa9, 0xe0,
                    0x93, 0xf3, 0xa3, 0xb5, 0x03, 0x00, 0x40, 0x6e},
    },
};

static esp_ble_scan_params_t ble_scan_params = {
    .scan_type = BLE_SCAN_TYPE_ACTIVE,
    .own_addr_type = BLE_ADDR_TYPE_PUBLIC,
    .scan_filter_policy = BLE_SCAN_FILTER_ALLOW_ALL,
    .scan_interval = 0x200,
    .scan_window = 0x50,
    .scan_duplicate = BLE_SCAN_DUPLICATE_DISABLE};

typedef struct gattc_profile_inst
{
    esp_gattc_cb_t gattc_cb;
    uint16_t gattc_if; // if = interface
    uint16_t app_id;
    uint16_t conn_id;
    uint16_t service_start_handle;
    uint16_t service_end_handle;
    uint16_t tx_char_handle;
    uint16_t rx_char_handle; // 40003,server用notify发送消息给client。
    esp_bd_addr_t remote_bda;
    esp_ble_addr_type_t remote_addr_type;
    bool is_service_connect;
    bool is_phy_connect;
} gattc_profile_inst_t;

static uint32_t duration = 30;

/* One gatt-based profile one app_id and one gattc_if, this array will store the gattc_if returned by ESP_GATTS_REG_EVT */
gattc_profile_inst_t gl_profile_tab[PROFILE_NUM] = {
    [PROFILE_GUNPOINT_ID] = {
        .gattc_cb = gattc_profile_event_handler,
        .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
        .remote_addr_type = BLE_ADDR_TYPE_PUBLIC,
        .is_service_connect = false,
        .is_phy_connect = false,
    },
    [PROFILE_GUNSCREEN_ID] = {
        .gattc_cb = gattc_profile_event_handler,
        .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
        .remote_addr_type = BLE_ADDR_TYPE_PUBLIC,
        .is_service_connect = false,
        .is_phy_connect = false,
    },
    [PROFILE_ARMOR_ID] = {
        .gattc_cb = gattc_profile_event_handler,
        .gattc_if = ESP_GATT_IF_NONE, /* Not get the gatt_if, so initial is ESP_GATT_IF_NONE */
        .remote_addr_type = BLE_ADDR_TYPE_PUBLIC,
        .is_service_connect = false,
        .is_phy_connect = false,
    },

};

// bool physical_connect_flag = false;
// bool get_server = false;
// 回调函数指针结构体
client_func_t client_fun;

client_func_t *get_client_func_p(void)
{
    client_func_t *p = &client_fun;
    return p;
}

static void gattc_profile_event_handler(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
{
    esp_ble_gattc_cb_param_t *p_data = (esp_ble_gattc_cb_param_t *)param;
    // ESP_LOGI(GATTC_TAG, "event: %d", event);
    switch (event)
    {
    case ESP_GATTC_REG_EVT: // 注册了客户端应用
        // ESP_LOGI(GATTC_TAG, "REG_EVT,gattc_if: %d", gattc_if);
        break;
    case ESP_GATTC_CONNECT_EVT:
    {
        // 一个设备连接上时，每个if都会触发这个事件，这里不能区分是哪个设备连接上了，除非用
        uint8_t equip_sn = 0xff;
        if (gattc_if == gl_profile_tab[PROFILE_GUNPOINT_ID].gattc_if &&
            memcmp(gl_profile_tab[PROFILE_GUNPOINT_ID].remote_bda, p_data->connect.remote_bda, 6) == 0)
        {
            equip_sn = PROFILE_GUNPOINT_ID;
        }
        else if (gattc_if == gl_profile_tab[PROFILE_GUNSCREEN_ID].gattc_if &&
                 memcmp(gl_profile_tab[PROFILE_GUNSCREEN_ID].remote_bda, p_data->connect.remote_bda, 6) == 0)
        {
            equip_sn = PROFILE_GUNSCREEN_ID;
        }
        else if (gattc_if == gl_profile_tab[PROFILE_ARMOR_ID].gattc_if &&
                 memcmp(gl_profile_tab[PROFILE_ARMOR_ID].remote_bda, p_data->connect.remote_bda, 6) == 0)
        {
            equip_sn = PROFILE_ARMOR_ID;
        }
        else
        {
            // 一个设备连接，三个if都会触发一次函数，只有和interface匹配的时候才用回调。
        }
        if (equip_sn != 0xff)
        {
            gl_profile_tab[equip_sn].is_phy_connect = true;
            gl_profile_tab[equip_sn].conn_id = p_data->connect.conn_id;
            esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req(gattc_if, p_data->connect.conn_id);
            if (mtu_ret)
            {
                ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret);
            }
        }
        break;
    }
    case ESP_GATTC_OPEN_EVT:
    { /*!< When GATT virtual connection is set up, the event comes */
        uint8_t equip_sn = 0xff;
        if (gattc_if == gl_profile_tab[PROFILE_GUNPOINT_ID].gattc_if)
        {
            equip_sn = PROFILE_GUNPOINT_ID;
        }
        else if (gattc_if == gl_profile_tab[PROFILE_GUNSCREEN_ID].gattc_if)
        {
            equip_sn = PROFILE_GUNSCREEN_ID;
        }
        else if (gattc_if == gl_profile_tab[PROFILE_ARMOR_ID].gattc_if)
        {
            equip_sn = PROFILE_ARMOR_ID;
        }
        else
        {
            ESP_LOGE(GATTC_TAG, "open failed, if error: %d", equip_sn);
        }
        if (param->open.status != ESP_GATT_OK && param->open.status != ESP_GATT_ALREADY_OPEN)
        {
            ESP_LOGE(GATTC_TAG, " ERROR: open.status: %d", param->open.status);
            gl_profile_tab[equip_sn].is_service_connect = false;
            client_fun.open_cb(equip_sn, param->open.remote_bda, false);
        }
        else
        {
            gl_profile_tab[equip_sn].is_phy_connect = true;
            gl_profile_tab[equip_sn].is_service_connect = true;
            memcpy(gl_profile_tab[equip_sn].remote_bda, param->open.remote_bda, 6);
            client_fun.open_cb(equip_sn, param->open.remote_bda, true);
            esp_err_t mtu_ret = esp_ble_gattc_send_mtu_req(gattc_if, p_data->connect.conn_id);
            if (mtu_ret)
            {
                ESP_LOGE(GATTC_TAG, "config MTU error, error code = %x", mtu_ret);
            }
        }
        break;
    }
    case ESP_GATTC_DIS_SRVC_CMPL_EVT:
    {
        if (param->dis_srvc_cmpl.status != ESP_GATT_OK)
        {
            ESP_LOGE(GATTC_TAG, "discover service failed, status %d", param->dis_srvc_cmpl.status);
            break;
        }
        // ESP_LOGI(GATTC_TAG, "discover service complete conn_id %d", param->dis_srvc_cmpl.conn_id);
        esp_ble_gattc_search_service(gattc_if, param->cfg_mtu.conn_id, &server_service_uuid);
        break;
    }
    case ESP_GATTC_CFG_MTU_EVT:
    {
        if (param->cfg_mtu.status != ESP_GATT_OK)
        {
            ESP_LOGE(GATTC_TAG, "config mtu failed, error status = %x", param->cfg_mtu.status);
        }
        // ESP_LOGI(GATTC_TAG, "ESP_GATTC_CFG_MTU_EVT, Status %d, MTU %d, conn_id %d", param->cfg_mtu.status, param->cfg_mtu.mtu, param->cfg_mtu.conn_id);
        break;
    }
    case ESP_GATTC_SEARCH_RES_EVT:
    {
        // ESP_LOGI(GATTC_TAG, "SEARCH RES: conn_id = %x is primary service %d", p_data->search_res.conn_id, p_data->search_res.is_primary);
        // ESP_LOGI(GATTC_TAG, "start handle %d end handle %d current handle value %d",
        // p_data->search_res.start_handle, p_data->search_res.end_handle, p_data->search_res.srvc_id.inst_id);
        // ESP_LOGI(GATTC_TAG,"service uuid len is :%d",p_data->search_res.srvc_id.uuid.len);
        if (p_data->search_res.srvc_id.uuid.len == ESP_UUID_LEN_128)
        {
            if (strncmp((char *)p_data->search_res.srvc_id.uuid.uuid.uuid128, (char *)server_service_uuid.uuid.uuid128, 16) == 0)
            {
                uint8_t equip_sn = 0xff;
                if (gattc_if == gl_profile_tab[PROFILE_GUNPOINT_ID].gattc_if)
                {
                    equip_sn = PROFILE_GUNPOINT_ID;
                }
                else if (gattc_if == gl_profile_tab[PROFILE_GUNSCREEN_ID].gattc_if)
                {
                    equip_sn = PROFILE_GUNSCREEN_ID;
                }
                else if (gattc_if == gl_profile_tab[PROFILE_ARMOR_ID].gattc_if)
                {
                    equip_sn = PROFILE_ARMOR_ID;
                }
                else
                {
                    ESP_LOGE(GATTC_TAG, "ESP_GATTC_SEARCH_RES_EVT if error: %d", equip_sn);
                }
                // ESP_LOGI(GATTC_TAG, "%d service found", equip_sn);
                gl_profile_tab[equip_sn].service_start_handle = p_data->search_res.start_handle;
                gl_profile_tab[equip_sn].service_end_handle = p_data->search_res.end_handle;
            }
            else
            {
                // ESP_LOGE(GATTC_TAG, "raw UUID128: %x", p_data->search_res.srvc_id.uuid.uuid.uuid128);
            }
        }
        else
        {
            // ESP_LOGI(GATTC_TAG, "raw UUID: %x", p_data->search_res.srvc_id.uuid.uuid.uuid128);
        }
        break;
    }
    case ESP_GATTC_SEARCH_CMPL_EVT:
    {
        if (p_data->search_cmpl.status != ESP_GATT_OK)
        {
            ESP_LOGE(GATTC_TAG, "search service failed, error status = %x", p_data->search_cmpl.status);
            break;
        }
        if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_REMOTE_DEVICE)
        {
            // ESP_LOGI(GATTC_TAG, "Get service information from remote device");
        }
        else if (p_data->search_cmpl.searched_service_source == ESP_GATT_SERVICE_FROM_NVS_FLASH)
        {
            // ESP_LOGI(GATTC_TAG, "Get service information from flash");
        }
        else
        {
            // ESP_LOGI(GATTC_TAG, "unknown service source");
        }
        // ESP_LOGI(GATTC_TAG, "ESP_GATTC_SEARCH_CMPL_EVT");
        uint8_t equip_sn = 0xff;
        if (gattc_if == gl_profile_tab[PROFILE_GUNPOINT_ID].gattc_if)
        {
            equip_sn = PROFILE_GUNPOINT_ID;
        }
        else if (gattc_if == gl_profile_tab[PROFILE_GUNSCREEN_ID].gattc_if)
        {
            equip_sn = PROFILE_GUNSCREEN_ID;
        }
        else if (gattc_if == gl_profile_tab[PROFILE_ARMOR_ID].gattc_if)
        {
            equip_sn = PROFILE_ARMOR_ID;
        }
        else
        {
            ESP_LOGE(GATTC_TAG, "ERROR GATTC_IF in ESP_GATTC_SEARCH_CMPL_EVT gattc_if:%d", gattc_if);
            ESP_LOGE(GATTC_TAG, "GP gattc_if:%d, GS gattc_if:%d, AR gattc_if:%d",
                     gl_profile_tab[PROFILE_GUNPOINT_ID].gattc_if,
                     gl_profile_tab[PROFILE_GUNSCREEN_ID].gattc_if,
                     gl_profile_tab[PROFILE_ARMOR_ID].gattc_if);
        }
        if (equip_sn != 0xff && gl_profile_tab[equip_sn].is_service_connect)
        {
            uint16_t count = 1;
            esp_gattc_char_elem_t rx_char_result;
            esp_gatt_status_t status = esp_ble_gattc_get_char_by_uuid(gattc_if,
                                                                      p_data->search_cmpl.conn_id,
                                                                      gl_profile_tab[equip_sn].service_start_handle,
                                                                      gl_profile_tab[equip_sn].service_end_handle,
                                                                      rx_char_uuid,
                                                                      &rx_char_result, &count);
            if (status != ESP_OK)
            {
                ESP_LOGE(GATTC_TAG, "FIND RX CHAR ERROR, SET CORRECT RX CHAR UUID");
            }
            else
            {
                gl_profile_tab[equip_sn].rx_char_handle = rx_char_result.char_handle;
                esp_ble_gattc_register_for_notify(gattc_if, gl_profile_tab[equip_sn].remote_bda,
                                                  rx_char_result.char_handle);
                // ESP_LOGI(GATTC_TAG, "FIND RXCHAR SUCCESS,if:%d, handle: %d.",equip_sn, gl_profile_tab[equip_sn].rx_char_handle);
            }
            esp_gattc_char_elem_t tx_char_result;
            status = esp_ble_gattc_get_char_by_uuid(gattc_if,
                                                    p_data->search_cmpl.conn_id,
                                                    gl_profile_tab[equip_sn].service_start_handle,
                                                    gl_profile_tab[equip_sn].service_end_handle,
                                                    tx_char_uuid,
                                                    &tx_char_result, &count);
            if (status != ESP_OK)
            {
                ESP_LOGE(GATTC_TAG, "FIND TX CHAR ERROR, SET CORRECT TX CHAR UUID");
            }
            else
            {
                gl_profile_tab[equip_sn].tx_char_handle = tx_char_result.char_handle;
                // ESP_LOGI(GATTC_TAG, "FIND TXCHAR SUCCESS,if:%d, handle: %d.", equip_sn, gl_profile_tab[equip_sn].tx_char_handle);
            }
        }
        break;
    }
    case ESP_GATTC_REG_FOR_NOTIFY_EVT:
    {
        // ESP_LOGI(GATTC_TAG, "ESP_GATTC_REG_FOR_NOTIFY_EVT");
        if (p_data->reg_for_notify.status != ESP_GATT_OK)
        {
            ESP_LOGE(GATTC_TAG, "REG FOR NOTIFY failed: error status = %d", p_data->reg_for_notify.status);
        }
        break;
    }
    case ESP_GATTC_NOTIFY_EVT:
    {
        // server 发送notify消息时触发
        if (p_data->notify.is_notify)
        {
            // ESP_LOGI(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, receive notify value:");
        }
        else
        {
            // ESP_LOGI(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT, receive indicate value:");
        }
        uint8_t equip_sn = 0xff;
        if (gattc_if == gl_profile_tab[PROFILE_GUNPOINT_ID].gattc_if)
        {
            equip_sn = PROFILE_GUNPOINT_ID;
        }
        else if (gattc_if == gl_profile_tab[PROFILE_GUNSCREEN_ID].gattc_if)
        {
            equip_sn = PROFILE_GUNSCREEN_ID;
        }
        else if (gattc_if == gl_profile_tab[PROFILE_ARMOR_ID].gattc_if)
        {
            equip_sn = PROFILE_ARMOR_ID;
        }
        else
        {
            ESP_LOGE(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT if error: %d", equip_sn);
        }
        // esp_log_buffer_hex(GATTC_TAG, p_data->notify.value, p_data->notify.value_len);
        client_fun.notify_cb(equip_sn, p_data->notify.remote_bda, p_data->notify.value, p_data->notify.value_len);
        break;
    }
    case ESP_GATTC_WRITE_DESCR_EVT:
    {
        if (p_data->write.status != ESP_GATT_OK)
        {
            ESP_LOGE(GATTC_TAG, "write descr failed, error status = %x", p_data->write.status);
            break;
        }
        // ESP_LOGI(GATTC_TAG, "write descr success ");
        break;
    }
    case ESP_GATTC_SRVC_CHG_EVT:
    {
        esp_bd_addr_t bda;
        memcpy(bda, p_data->srvc_chg.remote_bda, sizeof(esp_bd_addr_t));
        // ESP_LOGI(GATTC_TAG, "ESP_GATTC_SRVC_CHG_EVT, bd_addr:");
        // esp_log_buffer_hex(GATTC_TAG, bda, sizeof(esp_bd_addr_t));
        break;
    }
    case ESP_GATTC_WRITE_CHAR_EVT:
    {
        if (p_data->write.status != ESP_GATT_OK)
        {
            ESP_LOGE(GATTC_TAG, "write char failed, conn id:%d. handle: %d. error status = %x",
                     p_data->write.handle, p_data->write.conn_id, p_data->write.status);
            break;
        }
        else
        {
            // ESP_LOGE(GATTC_TAG, "write char success, conn id:%d. handle: %d. error status = %x",
            //          p_data->write.handle, p_data->write.conn_id, p_data->write.status);
        }
        break;
    }
    case ESP_GATTC_DISCONNECT_EVT:
    {
        uint8_t equip_sn = 0xff;

        if (gattc_if == gl_profile_tab[PROFILE_GUNPOINT_ID].gattc_if &&
            memcmp(gl_profile_tab[PROFILE_GUNPOINT_ID].remote_bda, p_data->disconnect.remote_bda, 6) == 0)
        {
            equip_sn = PROFILE_GUNPOINT_ID;
        }
        else if (gattc_if == gl_profile_tab[PROFILE_GUNSCREEN_ID].gattc_if &&
                 memcmp(gl_profile_tab[PROFILE_GUNSCREEN_ID].remote_bda, p_data->disconnect.remote_bda, 6) == 0)
        {
            equip_sn = PROFILE_GUNSCREEN_ID;
        }
        else if (gattc_if == gl_profile_tab[PROFILE_ARMOR_ID].gattc_if &&
                 memcmp(gl_profile_tab[PROFILE_ARMOR_ID].remote_bda, p_data->disconnect.remote_bda, 6) == 0)
        {
            equip_sn = PROFILE_ARMOR_ID;
        }
        else
        {
            // ESP_LOGE(GATTC_TAG, "ESP_GATTC_NOTIFY_EVT if error: %d", equip_sn);
            // 一个设备断开，三个if都会触发一次函数，只有和interface匹配的时候才用回调。
        }
        if (equip_sn != 0xff)
        {
            gl_profile_tab[equip_sn].is_phy_connect = false;
            gl_profile_tab[equip_sn].is_service_connect = false;
            client_fun.disconn_cb(equip_sn, p_data->disconnect.remote_bda, p_data->disconnect.reason);
        }
        break;
    }
    default:
        break;
    }
}

static void esp_gap_cb(esp_gap_ble_cb_event_t event, esp_ble_gap_cb_param_t *param)
{
    uint8_t *adv_name = NULL;
    uint8_t adv_name_len = 0;
    switch (event)
    {
    case ESP_GAP_BLE_SCAN_PARAM_SET_COMPLETE_EVT:
    {
        // the unit of the duration is second
        esp_ble_gap_start_scanning(duration);
        break;
    }
    case ESP_GAP_BLE_SCAN_START_COMPLETE_EVT:
        // scan start complete event to indicate scan start successfully or failed
        if (param->scan_start_cmpl.status != ESP_BT_STATUS_SUCCESS)
        {
            ESP_LOGE(GATTC_TAG, "scan start failed, error status = %x", param->scan_start_cmpl.status);
            break;
        }
        // ESP_LOGI(GATTC_TAG, "scan start success");

        break;
    case ESP_GAP_BLE_SCAN_RESULT_EVT:
    {
        esp_ble_gap_cb_param_t *scan_result = (esp_ble_gap_cb_param_t *)param;
        switch (scan_result->scan_rst.search_evt)
        {
        case ESP_GAP_SEARCH_INQ_RES_EVT:{
            uint8_t try_to_connect = 0;
            adv_name = esp_ble_resolve_adv_data(scan_result->scan_rst.ble_adv,
                                                ESP_BLE_AD_TYPE_NAME_CMPL, &adv_name_len);
            if (adv_name_len != 0 && scan_result->scan_rst.rssi < 0 && scan_result->scan_rst.rssi > RSSI_LOWEST)
            {
                client_fun.scan_result_cb(scan_result->scan_rst.bda, adv_name, adv_name_len, scan_result->scan_rst.rssi, &try_to_connect);
                if (try_to_connect) {
                    // esp_ble_gap_set_prefer_conn_params(scan_result->scan_rst.bda, 100, 300, 0, 600);
                }
            }
#if CONFIG_EXAMPLE_DUMP_ADV_DATA_AND_SCAN_RESP
            if (scan_result->scan_rst.adv_data_len > 0)
            {
                ESP_LOGI(GATTC_TAG, "adv data:");
                esp_log_buffer_hex(GATTC_TAG, &scan_result->scan_rst.ble_adv[0], scan_result->scan_rst.adv_data_len);
            }
            if (scan_result->scan_rst.scan_rsp_len > 0)
            {
                ESP_LOGI(GATTC_TAG, "scan resp:");
                esp_log_buffer_hex(GATTC_TAG, &scan_result->scan_rst.ble_adv[scan_result->scan_rst.adv_data_len], scan_result->scan_rst.scan_rsp_len);
            }
#endif
            break;
        }
            
        case ESP_GAP_SEARCH_INQ_CMPL_EVT:
            break;
        default:
            break;
        }
        break;
    }
    case ESP_GAP_BLE_SCAN_STOP_COMPLETE_EVT:
        if (param->scan_stop_cmpl.status != ESP_BT_STATUS_SUCCESS)
        {
            ESP_LOGE(GATTC_TAG, "scan stop failed, error status = %x", param->scan_stop_cmpl.status);
            break;
        }
        // ESP_LOGI(GATTC_TAG, "stop scan successfully");
        break;

    case ESP_GAP_BLE_ADV_STOP_COMPLETE_EVT:
        if (param->adv_stop_cmpl.status != ESP_BT_STATUS_SUCCESS)
        {
            ESP_LOGE(GATTC_TAG, "adv stop failed, error status = %x", param->adv_stop_cmpl.status);
            break;
        }
        // ESP_LOGI(GATTC_TAG, "stop adv successfully");
        break;
    case ESP_GAP_BLE_UPDATE_CONN_PARAMS_EVT:
        ESP_LOGI(GATTC_TAG, "update connection params status = %d, min_int = %d, max_int = %d,conn_int = %d,latency = %d, timeout = %d",
                 param->update_conn_params.status,
                 param->update_conn_params.min_int,
                 param->update_conn_params.max_int,
                 param->update_conn_params.conn_int,
                 param->update_conn_params.latency,
                 param->update_conn_params.timeout);
        break;
    default:
        break;
    }
}

static void esp_gattc_cb(esp_gattc_cb_event_t event, esp_gatt_if_t gattc_if, esp_ble_gattc_cb_param_t *param)
{
    /* If event is register event, store the gattc_if for each profile */
    if (event == ESP_GATTC_REG_EVT)
    {
        if (param->reg.status == ESP_GATT_OK)
        {
            gl_profile_tab[param->reg.app_id].gattc_if = gattc_if;
        }
        else
        {
            ESP_LOGI(GATTC_TAG, "reg app failed, app_id %04x, status %d",
                     param->reg.app_id,
                     param->reg.status);
            return;
        }
    }

    /* If the gattc_if equal to profile A, call profile A cb handler,
     * so here call each profile's callback */
    do
    {
        int idx;
        for (idx = 0; idx < PROFILE_NUM; idx++)
        {
            if (gattc_if == ESP_GATT_IF_NONE || /* ESP_GATT_IF_NONE, not specify a certain gatt_if, need to call every profile cb function */
                gattc_if == gl_profile_tab[idx].gattc_if)
            {
                if (gl_profile_tab[idx].gattc_cb)
                {
                    gl_profile_tab[idx].gattc_cb(event, gattc_if, param);
                }
            }
        }
    } while (0);
}

void ble_client_init(void)
{
    esp_err_t ret;
    ESP_ERROR_CHECK(esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT));

    esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
    ret = esp_bt_controller_init(&bt_cfg);
    if (ret)
    {
        ESP_LOGE(GATTC_TAG, "%s initialize controller failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }

    ret = esp_bt_controller_enable(ESP_BT_MODE_BLE);
    if (ret)
    {
        ESP_LOGE(GATTC_TAG, "%s enable controller failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }

    ret = esp_bluedroid_init();
    if (ret)
    {
        ESP_LOGE(GATTC_TAG, "%s init bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }

    ret = esp_bluedroid_enable();
    if (ret)
    {
        ESP_LOGE(GATTC_TAG, "%s enable bluetooth failed: %s\n", __func__, esp_err_to_name(ret));
        return;
    }

    // register the  callback function to the gap module
    ret = esp_ble_gap_register_callback(esp_gap_cb);
    if (ret)
    {
        ESP_LOGE(GATTC_TAG, "%s gap register failed, error code = %x\n", __func__, ret);
        return;
    }

    // register the callback function to the gattc module
    ret = esp_ble_gattc_register_callback(esp_gattc_cb);
    if (ret)
    {
        ESP_LOGE(GATTC_TAG, "%s gattc register failed, error code = %x\n", __func__, ret);
        return;
    }
    esp_err_t local_mtu_ret = esp_ble_gatt_set_local_mtu(180);
    if (local_mtu_ret)
    {
        ESP_LOGE(GATTC_TAG, "set local  MTU failed, error code = %x", local_mtu_ret);
    }

    ret = esp_ble_gattc_app_register(PROFILE_GUNPOINT_ID);
    if (ret)
    {
        ESP_LOGE(GATTC_TAG, "%s gattc app register failed, error code = %x\n", __func__, ret);
    }
    ret = esp_ble_gattc_app_register(PROFILE_GUNSCREEN_ID);
    if (ret)
    {
        ESP_LOGE(GATTC_TAG, "%s gattc app register failed, error code = %x\n", __func__, ret);
    }
    ret = esp_ble_gattc_app_register(PROFILE_ARMOR_ID);
    if (ret)
    {
        ESP_LOGE(GATTC_TAG, "%s gattc app register failed, error code = %x\n", __func__, ret);
    }
}

void set_remote_bda(uint8_t equip_sn, esp_bd_addr_t remote_bda)
{
    memcpy(gl_profile_tab[equip_sn].remote_bda, remote_bda, sizeof(esp_bd_addr_t));
}

void clean_remote_bda(uint8_t equip_sn)
{
    esp_bd_addr_t bda[6] = {0};
    memcpy(gl_profile_tab[equip_sn].remote_bda, bda, sizeof(esp_bd_addr_t));
}

bool is_physical_connected(uint8_t equip_sn)
{
    return gl_profile_tab[equip_sn].is_phy_connect;
}

bool is_virual_connected(uint8_t equip_sn)
{
    return gl_profile_tab[equip_sn].is_service_connect;
}

void close_physical_connection(uint8_t equip_sn)
{
    esp_err_t ret;
    ret = esp_ble_gap_disconnect(gl_profile_tab[equip_sn].remote_bda);
    // ESP_LOGI(GATTC_TAG, "esp_ble_gap_disconnect ret: %s",esp_err_to_name(ret));
}

void start_connect(uint8_t equip_sn)
{
    esp_ble_gattc_open(gl_profile_tab[equip_sn].gattc_if,
                       gl_profile_tab[equip_sn].remote_bda,
                       gl_profile_tab[equip_sn].remote_addr_type,
                       true);
}

void ble_client_set_scan_param(uint32_t scan_duration)
{
    duration = scan_duration;
    esp_err_t scan_ret = esp_ble_gap_set_scan_params(&ble_scan_params);
    if (scan_ret)
    {
        ESP_LOGE(GATTC_TAG, "set scan params error, error code = %x", scan_ret);
    }
}

void stop_scan(void)
{
    // ESP_LOGE(GATTC_TAG, "stop_scan");
    esp_ble_gap_stop_scanning();
}

esp_err_t write_char(uint8_t equip_sn, uint8_t *package, uint16_t len)
{
    esp_err_t ret = ESP_FAIL;
    if (is_virual_connected(equip_sn))
    {
        ret = esp_ble_gattc_write_char(gl_profile_tab[equip_sn].gattc_if,
                                       gl_profile_tab[equip_sn].conn_id,
                                       gl_profile_tab[equip_sn].tx_char_handle,
                                       len,
                                       package,
                                       //    ESP_GATT_WRITE_TYPE_RSP, // 应该需要有回应
                                       ESP_GATT_WRITE_TYPE_NO_RSP,
                                       ESP_GATT_AUTH_REQ_NONE);
    }
    else
    {
        // ESP_LOGI(GATTC_TAG, "sn %d device no connected", equip_sn);
    }
    return ret;
}
